package com.dynamicthreadpool.strategy;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

/**
 * AbortPolicyWithReport拒绝策略实现
 * 基于Dubbo 2.7版本提取，在拒绝任务时打印详细信息并转储线程栈
 */
public class AbortPolicyWithReport implements RejectStrategy {

    private final String threadName;
    private final String dumpPath;
    private final long maxQueueCapacity;
    private volatile long lastReportTime = 0;
    private static final long REPORT_INTERVAL = TimeUnit.MINUTES.toMillis(1);
    private static final String DUMP_PREFIX = "threadDump-";
    private static final String DUMP_SUFFIX = ".txt";

    public AbortPolicyWithReport() {
        this("dynamic-threadpool");
    }

    public AbortPolicyWithReport(String threadName) {
        this(threadName, System.getProperty("user.home") + File.separator + "logs");
    }

    public AbortPolicyWithReport(String threadName, String dumpPath) {
        this(threadName, dumpPath, 0);
    }

    public AbortPolicyWithReport(String threadName, String dumpPath, long maxQueueCapacity) {
        this.threadName = threadName;
        this.dumpPath = dumpPath;
        this.maxQueueCapacity = maxQueueCapacity;
    }

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        String msg = String.format(
                "[Thread Pool] Thread pool is EXHAUSTED! Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d), Queue: %d/%d, Task capacity rejection threshold: %d",
                threadName,
                executor.getPoolSize(),
                executor.getActiveCount(),
                executor.getCorePoolSize(),
                executor.getMaximumPoolSize(),
                executor.getLargestPoolSize(),
                executor.getTaskCount(),
                executor.getCompletedTaskCount(),
                executor.getQueue().size(),
                maxQueueCapacity,
                maxQueueCapacity
        );

        // 每分钟最多打印一次警告信息，避免日志风暴
        long now = System.currentTimeMillis();
        if (now - lastReportTime >= REPORT_INTERVAL) {
            lastReportTime = now;
            System.err.println(msg);
            // 转储线程栈
            dumpThreadDump();
        }

        throw new RejectedExecutionException(msg);
    }

    private void dumpThreadDump() {
        File dir = new File(dumpPath);
        if (!dir.exists() && !dir.mkdirs()) {
            System.err.println("Failed to create dump directory: " + dumpPath);
            return;
        }

        String timestamp = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(new Date());
        String dumpFileName = DUMP_PREFIX + threadName + "-" + timestamp + DUMP_SUFFIX;
        File dumpFile = new File(dir, dumpFileName);

        try {
            ThreadDump.dumpToFile(dumpFile);
            System.err.println("Thread dump generated: " + dumpFile.getAbsolutePath());
        } catch (IOException e) {
            System.err.println("Failed to generate thread dump: " + e.getMessage());
        }
    }

    @Override
    public String getPolicyName() {
        return "AbortPolicyWithReport";
    }

    /**
     * 线程栈转储工具类
     */
    private static class ThreadDump {
        public static void dumpToFile(File file) throws IOException {
            // 简化的线程栈转储实现
            ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
            ThreadInfo[] threadInfos = mxBean.dumpAllThreads(true, true);

            try (PrintWriter writer = new PrintWriter(new FileWriter(file))) {
                for (ThreadInfo info : threadInfos) {
                    writer.println("Thread: " + info.getThreadName() + " (ID: " + info.getThreadId() + ")");
                    writer.println("State: " + info.getThreadState());
                    writer.println("Stack Trace:");
                    for (StackTraceElement element : info.getStackTrace()) {
                        writer.println("  " + element.toString());
                    }
                    writer.println();
                }
            }
        }
    }
}